using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if UNITY_ANDROID
using Unity.Notifications.Android;
using Notifications.Runtime.Android;
#elif UNITY_IOS
using Notifications.Runtime.iOS;
#endif
using UnityEngine;

namespace Notifications.Runtime
{
    /// <summary>
    /// Global notifications manager that serves as a wrapper for multiple platforms' notification systems.
    /// </summary>
    public class GameNotificationsManager : MonoBehaviour
    {
        private static GameNotificationsManager _instance;
 
        private static readonly object _lock = new object();
        public static GameNotificationsManager Instance
        {
            get
            {
                lock (_lock)
                {
                    // _instance = FindObjectOfType(typeof(GameNotificationsManager)) as GameNotificationsManager;
                    if (_instance == null)
                    {
                        GameObject singletonGo = new GameObject("SingletonMono" + typeof(GameNotificationsManager).Name);
                        _instance = singletonGo.AddComponent<GameNotificationsManager>();
                        _instance.Awake();
                        DontDestroyOnLoad(singletonGo);
					
                    }
                    return _instance;
                }
            }
        }

        private void Awake()
        {
            if (!Initialized)
            {
                _instance = this;
                var c1 = new GameNotificationChannel(ChannelId, ChannelId, "Generic notifications");
                _instance.Initialize(c1);
            }
        }

        public const string ChannelId = "game_channel0";
        [Flags]
        public enum OperatingMode
        {
            /// <summary>
            /// Do not perform any queueing at all. All notifications are scheduled with the operating system
            /// immediately.
            /// </summary>
            NoQueue = 0x00,
            /// <summary>
            /// <para>
            /// Queue messages that are scheduled with this manager.
            /// No messages will be sent to the operating system until the application is backgrounded.
            /// </para>
            /// <para>
            /// If badge numbers are not set, will automatically increment them. This will only happen if NO badge numbers
            /// for pending notifications are ever set.
            /// </para>
            /// </summary>
            // Queue = 0x01,
            /// <summary>
            /// When the application is foregrounded, clear all pending notifications.
            /// </summary>
            // ClearOnForegrounding = 0x02,
        }

        // [SerializeField, Tooltip("The operating mode for the notifications manager.")]
        private OperatingMode mode = OperatingMode.NoQueue;

        /// <summary>
        /// Gets the implementation of the notifications for the current platform;
        /// </summary>
        public IGameNotificationsPlatform Platform { get; private set; }
        
        public IGameBackgroundNotificationsPlatform BackgroundNotificationsPlatform{ get; private set; }

        /// <summary>
        /// Gets the operating mode for this manager.
        /// </summary>
        /// <seealso cref="OperatingMode"/>
        public OperatingMode Mode => mode;

        /// <summary>
        /// Gets whether this manager automatically increments badge numbers.
        /// </summary>

        /// <summary>
        /// Gets whether this manager has been initialized.
        /// </summary>
        public bool Initialized { get; private set; }

        public static bool IsInstanceCreated => _instance != null;

        // Flag set when we're in the foreground
        private bool inForeground = true;

        /// <summary>
        /// Clean up platform object if necessary
        /// </summary>
        protected virtual void OnDestroy()
        {
            if (Platform == null)
            {
                return;
            }
            Platform.NotificationReceived -= OnNotificationReceived;
            Platform.Dispose();
            inForeground = false;
        }

        /// <summary>
        /// Respond to application foreground/background events.
        /// </summary>
        protected void OnApplicationPause(bool pause)
        {
            if (Platform == null || !Initialized)
            {
                return;
            }

            inForeground = !pause;
            if (inForeground)
            {
                OnForegrounding();
            }
            else
            {
                OnBackground();
            }
        }

        /// <summary>
        /// Initialize the notifications manager.
        /// </summary>
        /// <param name="channels">An optional collection of channels to register, for Android</param>
        /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has already been called.</exception>
        public void Initialize(params GameNotificationChannel[] channels)
        {
            if (Initialized)
            {
                throw new InvalidOperationException("NotificationsManager already initialized.");
            }

            Initialized = true;

#if UNITY_ANDROID
            Platform = new AndroidNotificationsPlatform();
            BackgroundNotificationsPlatform = new AndroidBackgroundNotificationManager();
            BackgroundNotificationsPlatform.Init();
            // Register the notification channels
            var doneDefault = false;
            foreach (GameNotificationChannel notificationChannel in channels)
            {
                if (!doneDefault)
                {
                    doneDefault = true;
                    ((AndroidNotificationsPlatform)Platform).DefaultChannelId = notificationChannel.Id;
                }

                long[] vibrationPattern = null;
                if (notificationChannel.VibrationPattern != null)
                    vibrationPattern = notificationChannel.VibrationPattern.Select(v => (long)v).ToArray();

                // Wrap channel in Android object
                var androidChannel = new AndroidNotificationChannel(notificationChannel.Id, notificationChannel.Name,
                    notificationChannel.Description,
                    (Importance)notificationChannel.Style)
                {
                    CanBypassDnd = notificationChannel.HighPriority,
                    CanShowBadge = notificationChannel.ShowsBadge,
                    EnableLights = notificationChannel.ShowLights,
                    EnableVibration = notificationChannel.Vibrates,
                    LockScreenVisibility = (LockScreenVisibility)notificationChannel.Privacy,
                    VibrationPattern = vibrationPattern
                };

                AndroidNotificationCenter.RegisterNotificationChannel(androidChannel);
            }
#elif UNITY_IOS
            Platform = new iOSNotificationsPlatform();
            BackgroundNotificationsPlatform = new iOSBackgroundNotificationManager();
            BackgroundNotificationsPlatform.Init();
#endif

            if (Platform != null)
            {
                Platform.NotificationReceived += OnNotificationReceived;
            }
            OnForegrounding();
        }

        
        /// <summary>
        /// Creates a new notification object for the current platform.
        /// </summary>
        /// <returns>The new notification, ready to be scheduled, or null if there's no valid platform.</returns>
        /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception>
        public IGameNotification CreateNotification()
        {
            return Platform?.CreateNotification();
        }
        
        public IGameBackgroundNotification CreateBackgroundNotification()
        {
            return BackgroundNotificationsPlatform?.CreateBackgroundNotification();
        }

        /// <summary>
        /// must called in main thread
        /// </summary>
        /// <param name="notification"></param>
        public void ScheduleBackgroundNotification(IGameBackgroundNotification notification)
        {
            if (notification == null || BackgroundNotificationsPlatform == null)
            {
                Debug.LogWarning("ScheduleNotification is not support on this platform:" + Application.platform.ToString());
                return;
            }
            BackgroundNotificationsPlatform.ScheduleBackgroundNotification(notification);
        }
        
        public void CancelBackgroundNotification(int id)
        {
            if (BackgroundNotificationsPlatform == null)
            {
                Debug.LogWarning("ScheduleNotification is not support on this platform:" + Application.platform.ToString());
                return;
            }
            BackgroundNotificationsPlatform.CancelBackgroundNotification(id);
        }

        public void CancelAllBackgroundNotifications()
        {
            if (BackgroundNotificationsPlatform == null)
            {
                Debug.LogWarning("ScheduleNotification is not support on this platform:" + Application.platform.ToString());
                return;
            }
            BackgroundNotificationsPlatform.CancelAllBackgroundNotifications();
        }
        
        
        /// <summary>
        /// update a exist background notification
        /// </summary>
        /// <param name="notification"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public void UpdateBackgroundNotification(IGameBackgroundNotification notification)
        {
            if (notification == null || Platform == null)
            {
                Debug.LogWarning("ScheduleNotification is not support on this platform:" + Application.platform.ToString());
            }
            BackgroundNotificationsPlatform.UpdateDateBackgroundNotification(notification);
        }

        /// <summary>
        /// Schedules a notification to be delivered.  only in main thread
        /// </summary>
        /// <param name="notification">The notification to deliver.</param>
        public IGameNotification ScheduleNotification(IGameNotification notification)
        {
            if (notification == null || Platform == null)
            {
                Debug.LogWarning("ScheduleNotification is not support on this platform:" + Application.platform.ToString());
                return null;
            }
            if (!notification.Id.HasValue)
            {
                // Generate an ID for items that don't have one (just so they can be identified later)
                int id = Math.Abs(DateTime.Now.ToString("yyMMddHHmmssffffff").GetHashCode());
                notification.Id = id;
            }
            Platform.ScheduleNotification(notification);
            return notification;
        }

        /// <summary>
        /// Cancels a scheduled notification.  only in main thread
        /// </summary>
        /// <param name="notificationId">The ID of the notification to cancel.</param>
        /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception>
        public void CancelNotification(int notificationId)
        {
            if (Platform == null)
            {
                return;
            }

            Platform.CancelNotification(notificationId);
        }

        /// <summary>
        /// Cancels all scheduled notifications.  only in main thread
        /// </summary>
        /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception>
        public void CancelAllNotifications()
        {
            if (Platform == null)
            {
                return;
            }

            Platform.CancelAllScheduledNotifications();
        }

        /// <summary>
        /// Dismisses a displayed notification.
        /// </summary>
        /// <param name="notificationId">The ID of the notification to dismiss.</param>
        /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception>
        public void DismissNotification(int notificationId)
        {
            Platform?.DismissNotification(notificationId);
        }

        /// <summary>
        /// Dismisses all displayed notifications.
        /// </summary>
        /// <exception cref="InvalidOperationException"><see cref="Initialize"/> has not been called.</exception>
        public void DismissAllNotifications()
        {
            Platform?.DismissAllDisplayedNotifications();
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public IGameNotification GetLastNotification()
        {
            return Platform?.GetLastNotification();
        }

        /// <summary>
        /// Event fired by <see cref="Platform"/> when a notification is received.
        /// </summary>
        private void OnNotificationReceived(IGameNotification deliveredNotification)
        {
                
        }

        // Clear foreground notifications and reschedule stuff from a file
        private void OnForegrounding()
        {
            Platform.OnForeground();
            BackgroundNotificationsPlatform.OnForeground();
            // Foregrounding
            // if ((mode & OperatingMode.ClearOnForegrounding) == OperatingMode.ClearOnForegrounding)
            // {
            //     // Clear on foregrounding
            //     Platform.CancelAllScheduledNotifications();
            // }
        }

        private void OnBackground()
        {
            Platform.OnBackground();
            BackgroundNotificationsPlatform.OnBackground();
        }
    }
}
